package de.tub.pong.main;

import java.util.ArrayList;
import java.util.List;

import processing.core.PApplet;
import SimpleOpenNI.SimpleOpenNI;
import SimpleOpenNI.XnSkeletonJointPosition;
import de.tub.pong.objects.Ball;
import de.tub.pong.objects.Racket;
import de.tub.pong.objects.ScoreBoard;

public class Pong extends PApplet {

	private static final long serialVersionUID = 1L;

	// key buffer
	private boolean leftKeyUpPressed = false;
	private boolean leftKeyDownPressed = false;
	private boolean leftKeyLeftPressed = false;
	private boolean leftKeyRightPressed = false;
	private boolean rightKeyUpPressed = false;
	private boolean rightKeyDownPressed = false;
	private boolean rightKeyLeftPressed = false;
	private boolean rightKeyRightPressed = false;

	// for testing you can play without cam
	private static final boolean playWithCam = true;
	public SimpleOpenNI context;

	private ScoreBoard scores;
	private Ball ball;
	private Racket racketLeft;
	private Racket racketRight;
	private List<Racket> rackets;

	public void setup() {

		size(800, 600);
		if (playWithCam)
			setupOpenNI();

		// processing
		frameRate(30);
		noStroke();

		racketLeft = new Racket(this, 1);
		racketRight = new Racket(this, 2);

		this.rackets = new ArrayList<Racket>();
		rackets.add(racketLeft);
		rackets.add(racketRight);

		ball = new Ball(this);

		scores = new ScoreBoard(this);

	}

	public void draw() {

		if (playWithCam) {
			processCamera();
			if (context.isTrackingSkeleton(1) && context.isTrackingSkeleton(2)) {
				updateBall(ball.getVelocity());
			}
		} else {
			background(0);
			processInputs();
			updateBall(ball.getVelocity());
		}

		renderPong();
	}

	private void updateBall(float range) {

		while (range > 0) {
			float simRange = range;
			simRange = min(simRange, collisionDetectionBorderTop());
			simRange = min(simRange, collisionDetectionBorderBottom());
			simRange = min(simRange, collisionDetectionRacketLeft());
			simRange = min(simRange, collisionDetectionRacketRight());

			// to avoid deadend loops
			if (simRange <= 0)
				simRange = 1;

			ball.simulate(simRange, rackets);
			range -= simRange;
		}
		if (range > 0)
			ball.simulate(range, rackets);
	}

	private float collisionDetectionBorderTop() {
		return ball.getUpperY();
	}

	private float collisionDetectionBorderBottom() {
		return height - (ball.getLowerY());
	}

	private float collisionDetectionRacketLeft() {
		return racketLeft.calcDistance(ball.getFrontEdgeX());
	}

	private float collisionDetectionRacketRight() {
		return racketRight.calcDistance(ball.getFrontEdgeX());
	}

	private void renderPong() {
		stroke(0);

		scores.draw();
		ball.draw();
		racketLeft.draw();
		racketRight.draw();
	}

	public void setupOpenNI() {
		context = new SimpleOpenNI(this, SimpleOpenNI.RUN_MODE_MULTI_THREADED);

		// enable depthMap generation
		context.enableDepth();
		context.enableUser(SimpleOpenNI.SKEL_PROFILE_ALL);
		context.setMirror(true);

		background(100, 0, 0);

		size(context.depthWidth(), context.depthHeight());
	}

	public void onNewUser(int userId) {
		System.out.println("found user number " + userId);
		System.out.println(context.loadCalibrationDataSkeleton(userId,
				"UserCalibration.bin"));
		context.startTrackingSkeleton(userId);
	}

	private void processCamera() {
		// update the cam
		context.update();

		if (context.isTrackingSkeleton(1)) {
			XnSkeletonJointPosition pos3d = new XnSkeletonJointPosition();
			XnSkeletonJointPosition pos2d = new XnSkeletonJointPosition();
			context.getJointPositionSkeleton(1, SimpleOpenNI.SKEL_LEFT_HAND,
					pos3d);
			context.convertRealWorldToProjective(pos3d.getPosition(), pos2d
					.getPosition());

			this.racketLeft.setCenterY(pos2d.getPosition().getY());
			this.racketLeft.setCenterX(pos2d.getPosition().getX());
		}
		if (context.isTrackingSkeleton(2)) { /* for 2 players */
			XnSkeletonJointPosition pos3d = new XnSkeletonJointPosition();
			XnSkeletonJointPosition pos2d = new XnSkeletonJointPosition();
			context.getJointPositionSkeleton(2, SimpleOpenNI.SKEL_RIGHT_HAND,
					pos3d);
			context.convertRealWorldToProjective(pos3d.getPosition(), pos2d
					.getPosition());

			this.racketRight.setCenterY(pos2d.getPosition().getY());
			this.racketRight.setCenterX(pos2d.getPosition().getX());
		}

		// draw depthImageMap
		image(context.depthImage(), 0, 0);
	}

	private void processInputs() {
		if (leftKeyUpPressed == true)
			racketLeft.moveUp();
		if (leftKeyDownPressed == true)
			racketLeft.moveDown();
		if (leftKeyLeftPressed == true)
			racketLeft.moveLeft();
		if (leftKeyRightPressed == true)
			racketLeft.moveRight();

		if (rightKeyUpPressed == true)
			racketRight.moveUp();
		if (rightKeyDownPressed == true)
			racketRight.moveDown();
		if (rightKeyLeftPressed == true)
			racketRight.moveLeft();
		if (rightKeyRightPressed == true)
			racketRight.moveRight();
	}

	public void raiseScore(int playerNumber) {
		if (playerNumber == 1) {
			scores.raiseScorePlayerLeft();
		} else {
			scores.raiseScorePlayerRight();
		}
	}

	public void keyPressed() {

		// player left
		if (key == 'w')
			leftKeyUpPressed = true;
		if (key == 's')
			leftKeyDownPressed = true;
		if (key == 'a')
			leftKeyLeftPressed = true;
		if (key == 'd')
			leftKeyRightPressed = true;

		// player right
		if (key == CODED) {
			if (keyCode == UP)
				rightKeyUpPressed = true;
			if (keyCode == DOWN)
				rightKeyDownPressed = true;
			if (keyCode == LEFT)
				rightKeyLeftPressed = true;
			if (keyCode == RIGHT)
				rightKeyRightPressed = true;
		}

		// velocity
		if (key == '+')
			ball.raiseVelocity();
		if (key == '-' && frameRate > 0)
			ball.reduceVelocity();
		// frameRate
		if (key == '*' && frameRate < 90)
			frameRate(frameRate++);
		if (key == '/' && frameRate > 0)
			frameRate(frameRate--);
	}

	public void keyReleased() {

		// player left
		if (key == 'w')
			leftKeyUpPressed = false;
		if (key == 's')
			leftKeyDownPressed = false;
		if (key == 'a')
			leftKeyLeftPressed = false;
		if (key == 'd')
			leftKeyRightPressed = false;

		// player right
		if (key == CODED) {
			if (keyCode == UP)
				rightKeyUpPressed = false;
			if (keyCode == DOWN)
				rightKeyDownPressed = false;
			if (keyCode == LEFT)
				rightKeyLeftPressed = false;
			if (keyCode == RIGHT)
				rightKeyRightPressed = false;
		}
	}

}
